package algo_annotations.data.sites;

import algo_annotations.data.annotations.Anno;
import algo_annotations.data.annotations.GroupeAnno;
import algo_annotations.data.contrainte_superposition.GroupeSegmentFixe;
import algo_annotations.data.sites.Site.TypePreplacement;
import algo_annotations.params.Params_a;
import commons.params_appli.ParamsAppli;
import commons.logs.Logger;
import commons.data.Zones;
import commons.spatialindex.simplegrid.SimpleGridSPI;
import commons.utils.UtilsGeom;
import commons.utils.primitive_geom.Rect_d;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;

public class GroupeSite {

	private Params_a params;
	public ArrayList<Site> vSite;
	private SimpleGridSPI spiSites;
	private Rect_d rectEmprise;
	// Plus grand cote du plus grand rect_larg des sites : necessaire pour utilisation du SPI
	//  par les sites pour precalcul intersection lors de l'optimisation
	private double largestDimensionRectMax_reel_m;
	private double largestRayon_reel_m;
	// version optimisée par zones (dans certaines zones, les rayons sont plus petits
	private HashMap<Zones,Double> mapLargestDimensionRectLarg;
	private HashMap<Zones,Double> mapLargestRayon;


	//**************************************************************************
	// PRE CALCULS DE PROXIMITE pour optimiser le calcul des notes etc.
	private boolean bHasBeenOptimized_precalculProximite = false;
	public void optimize_precalculProximite(GroupeSegmentFixe grpSeg) {
		assert grpSeg.hasBeenOptimized();
		preTraitementForOptimisation();

		for (Site site : vSite)
			site.optimize_precalculProximite (this, grpSeg);
		bHasBeenOptimized_precalculProximite = true;
	}


	//**************************************************************************
	// Sous decoupage du problème pour les passe 1 et 2

	//******* Sous-groupe pour les traitement de la passe 1 ********************
	// 1 sous groupe qui ne fera l'objet d'aucun calcul les libelles vides
	// on conserve les sites car leur emprise peut intervenir sur la superposition anno/site d'autres annos
	public SousGroupeSite sg_NoLibelle;

	// 2 sous groupe speciaux
	public SousGroupeSite sg_PlacementBest;
	public SousGroupeSite sg_PlacementAlmostBest;

	// autres sous-groupes connexe normaux
	public ArrayList<SousGroupeSite> v_SgConnexe_pass1;
	
	//******* Sous-groupe pour les traitement de la passe 2 ********************
	public ArrayList<SousGroupeSite> v_Sg_pass2;


	public int getNbSousGroupeConnexe_pass1() {
		return v_SgConnexe_pass1.size();
	}
	// -2 pour le groupe sans libellé
	// -1 pour le groupe parfaitement placé
	//  0 pour le groupe presque bien placé
	//  1->k pour le sous groupe connexe "normal" n°k
	public SousGroupeSite getSousGroupeSite_pass1 (int numSsGroupe) {
		if (numSsGroupe==-2)
			return sg_NoLibelle;
		if (numSsGroupe==-1)
			return sg_PlacementBest;
		if (numSsGroupe==0)
			return sg_PlacementAlmostBest;
		return v_SgConnexe_pass1.get(numSsGroupe-1);
	}


	public GroupeSite(Params_a params) {
		vSite = new ArrayList<Site>();
		this.params = params;
	}

	public int nbSite() {return vSite.size();}
	public int nbSite_withLibelle() {
		if (sg_NoLibelle!=null)
			return vSite.size() - sg_NoLibelle.size();
		else
			return vSite.size();
	}
	
	//L'ordre des Site est tres important et doit etre conservé durant tout le traitement
	// car leur place dans vSite les identifie dans les structures d'optimisation
	//--> Tout ajout de Site doit passer par cette methode
	public void addSite (Site s) {
		vSite.add (s);
		//on indique au site son index dans le groupe de site
		s.index = vSite.size()-1;
	}

	private void preTraitementForOptimisation() {
		computeRectEmprise();
		computeLargestObject();

		// On rempli l'index spatial des sites, car on en a besoin pour l'optimisation de chaque site
		if (rectEmprise != null) {
			spiSites = new SimpleGridSPI("SPI Sites", rectEmprise, SimpleGridSPI.getTailleFor(vSite.size()));
			spiSites.addObjects(vSite);
		}
	}

	
	//**************************************************************************
	// TRAITEMENT 1ère passe (passe principale)
	//**************************************************************************

	// Pour les sites, seules certaines optimisation de precalcul sont necessaires à ce stade :
	//  on ne calcule pas d'ambiguite, ou de superposition d'anno, etc...
	// Puis on cree un groupe d'anno preplacees
	// Pour les anno, on commence par calculer l'emplacement optimal, independamment des autres anno
	public void prepareTraitement_pass1(GroupeSegmentFixe grpSeg, Params_a params) {
		assert grpSeg.hasBeenOptimized();
		assert (bHasBeenOptimized_precalculProximite);

		if (ParamsAppli.getParam_b("Anno.UTILISE_DECOUPAGE_DONNEE_PASSE_1"))
			computeDecoupageProbleme_pass1(grpSeg, params);
		else
			makeOnlyOneSousGroupeSite_pass1();


		// Bilan des sous groupes créés
		for (int i=-2; i<=v_SgConnexe_pass1.size() ; i++) {
			SousGroupeSite sgs = getSousGroupeSite_pass1(i);
			if (sgs==null) continue;
			Logger.log_INFO2(" -> " + sgs.name + " : " + sgs.size());
			Logger.log_DEBUG1(sgs.printSite());
		}
	}

	
	// Pas de sous-decoupage du problème
	// La seule chose que l'on fait est de mettre à part les sites sans libelle
	private void makeOnlyOneSousGroupeSite_pass1() {
		sg_NoLibelle = new SousGroupeSite("Sous-groupe sites sans Libellé", this);

		v_SgConnexe_pass1 = new ArrayList<SousGroupeSite>();
		SousGroupeSite sgs = new SousGroupeSite("Sous-groupe sites avec Libelle", this);
		v_SgConnexe_pass1.add(sgs);

		for (Site site : vSite) {
			if (!site.hasAnno())
				sg_NoLibelle.addSite(site);
			else
				sgs.addSite(site);
		}
	}


	private void computeDecoupageProbleme_pass1(GroupeSegmentFixe grpSeg, Params_a params) {
		assert grpSeg.hasBeenOptimized();

		// Recherche de l'emplacement optimal, independament des autres sites/anno
		int nbTestPlacemet_X = ParamsAppli.getParam_i("Anno.PREPLACEMENTANNO_NBX");
		int nbTestPlacemet_Y = ParamsAppli.getParam_i("Anno.PREPLACEMENTANNO_NBY");

		for (Site site : vSite)
			site.computeBestPlacement_indepAutreAnno(nbTestPlacemet_X, nbTestPlacemet_Y);

		// Serie de passe pour pre-placer les sites dont l'anno optimale est totalement independante des autres
		// On fait plusieurs passe, car chaque fois qu'une site est pré-placé parfaitement, son rectangle
		//  de déplacement d'anno est réduit à l'anno bien placée : cela permet dans des passes
		//  ultérieures de trouver des sites qui deviennent indépendant, alors qu'ils ne l'étaient pas
		//  dans une passe précédente
		int nbAnnoIndependante = 0;
		boolean bNeedNewPasse = true;
		while (bNeedNewPasse) {
			bNeedNewPasse = false;
			for (Site site : vSite) {
				if (site.bIsPreplace()) continue;
				if (site.check_if_BestPosition_IndependantAutreAnno()) {
					//System.out.println("~~~~ " + site + " : best position independante");
					nbAnnoIndependante++;
					bNeedNewPasse = true;
				}
			}
			Logger.log_DEBUG1("====> NB ANNO indépendantes best position : " + nbAnnoIndependante);
		}

		// Recherche d'un emplacement légèrement dégradé, mais qui laisse l'anno totalement indépendante des autres
		// Là encore, on fait plusieurs passes mais en plus, à chaque passe, on re-recherche une position,
		//  car au fil des passes, des espaces libres apparaissent et on peut trouver des emplacements possibles
		//  alors qu'impossibles à la passe précédente
		nbAnnoIndependante = 0;
		bNeedNewPasse = true;

		// On divise le nombre de combinaisons, car on recherche une position approximative qui sera affinée par la suite
		double facteurDivision = (double) ParamsAppli.getParam_i("Anno.PREPLACEMENTANNO_FACTEURREDUCTION_PREPLACEMENT_APPROX");
		nbTestPlacemet_X /= Math.sqrt(facteurDivision);
		nbTestPlacemet_Y /= Math.sqrt(facteurDivision);

		// note seuil en deca de laquelle une annotation pré-placée approximativement est acceptée
		//  trop grand -> sous-découpage fonctionne mieux mais moins optimisé au global
		//  car le groupe des anno pré-placée approx devient très grand, or il s'agit d'anno
		//  qui peuvent avoir des intéractions avec les autres (notamment en frontière de groupe des anno approx)
		//  au niveau de leur emplacement optimal
		double seuilNote = ParamsAppli.getParam_d("Anno.SEUIL_NOTE_PREPLACEMENT_APPROX");

		while (bNeedNewPasse) {
			bNeedNewPasse = false;
			for (Site site : vSite)
				site.computeAlmostBestPlacement_noInteractionAutreAnno(nbTestPlacemet_X, nbTestPlacemet_Y);

			for (Site site : vSite) {
				if (site.bIsPreplace()) continue;
				if (site.check_if_BestPositionWithNoInteract_notToBad(seuilNote)) {
					//System.out.println("~~~~ " + site + " : best position independante");
					nbAnnoIndependante++;
					bNeedNewPasse = true;
				}
			}
			Logger.log_DEBUG1("====> NB ANNO indépendantes \"almost best\" position : " + nbAnnoIndependante);
		}

		//**********************************************************************
		// Découpage en composantes connexes d'interaction

		// Set des sites deja rangés dans un sous-groupe
		HashSet<Site> setSiteDejaRange = new HashSet<Site>();

		// D'abord les sous-groupe speciaux
		sg_NoLibelle = new SousGroupeSite("Ss-grpe sites sans libellé", this);
		sg_PlacementBest = new SousGroupeSite("Ss-grpe annos placées parfaitement, sans interaction", this);
		sg_PlacementAlmostBest = new SousGroupeSite("Ss-grpe annos placées approximativement, sans interaction", this);

		for (Site site : vSite) {
			if (!site.hasAnno()) {
				sg_NoLibelle.addSite(site);
				setSiteDejaRange.add(site);
			} else if (site.typePreplacement==TypePreplacement.BEST) {
				sg_PlacementBest.addSite(site);
				setSiteDejaRange.add(site);
			} else if (site.typePreplacement == TypePreplacement.ALMOSTBEST) {
				sg_PlacementAlmostBest.addSite(site);
				setSiteDejaRange.add(site);
			}
		}

		// Autres sous-groupes normaux
		v_SgConnexe_pass1 = new ArrayList<SousGroupeSite>();
		int numSousGroupe = 1;
		for (Site site : vSite) {
			if (setSiteDejaRange.contains(site)) continue;
			//site pas encore traité, on crée un nouveau sous groupe
			HashSet<Site> setSiteDuSousGroupe = new HashSet<Site>();
			completeSousGroupeAvecSitesInteraction_rec(site, setSiteDuSousGroupe, setSiteDejaRange, -1);

			v_SgConnexe_pass1.add(createSousGroupe("Ss-grpe composante connexe n°" + numSousGroupe + " ("+setSiteDuSousGroupe.size()+")", setSiteDuSousGroupe));
			numSousGroupe++;
		}
		assert(setSiteDejaRange.size() == vSite.size()) : ""+setSiteDejaRange.size() + " / " + vSite.size();

		// dernière étape : à partir des sous-groupe, on regroupe ceux qui sont trop petits
		//  car des groupes tres petits mettent du temps à etre resolus avec l'algo :
		// il y a bcp de temps fixe (create des thread de calcul a chaque step ...) du coup
		//  les 4 CPU sont loin des 100%
		v_SgConnexe_pass1 = regroupeSousGroupeTropPetits(v_SgConnexe_pass1, "passe 1");

}



	//**************************************************************************
	// TRAITEMENT 2ème passe : affinage sur points durs
	//**************************************************************************

	// On risque d'appeler plusieurs fois cette methode car en mode interactif, l'utilisateur
	//  peut simuler la creation des ssgroupe passe 2 en faisant varier les paramètres
	// Il faut donc tout bien re-nettoyer a chaque appel
	public void prepareTraitement_pass2(GroupeAnno bestGrpAnno, double seuilNoteAnno, double seuilIntersectRectMax) {
		Logger.log_INFO1("Préparation des traitements de la 2ème passe");
		Logger.log_INFO1(" - seuil note = " + seuilNoteAnno);
		Logger.log_INFO1(" - seuil interaction = " + seuilIntersectRectMax);
		// On va préparer des SousGroupe assez petits sur des zones avec de mauvaises notes
		//  (on ne s'étend pas sur toute la composante connexe, car ca reviendrait à ce qu'on
		//  a fait dans le traitement principal ...)
		// on arrete le sous groupe sur une série de seuil prédéfinis

		v_Sg_pass2 = new ArrayList<SousGroupeSite>();

		// set des sites deja rangés dans un sous-groupe
		HashSet<Site> setSiteDejaExamine = new HashSet<Site>();

		int numSousGroupe=1;
		while (true) {

			// Recherche pire site
			Anno pireAnno = bestGrpAnno.getPireAnno(seuilNoteAnno, setSiteDejaExamine);
			// on n'a plus trouvé d'anno pire que la note seuil et qui n'a pas encore été examinée
			if (pireAnno == null) break;
			Site pireSite = pireAnno.site;

			// Création d'un sous-groupe associé à ce site
			HashSet<Site> setSiteDuSousGroupe = new HashSet<Site>();
			completeSousGroupeAvecSitesInteraction_rec(pireSite, setSiteDuSousGroupe, setSiteDejaExamine, seuilIntersectRectMax);
			// Rq : il se peut que le sous-groupe soit reduit au pire site, si le site de départ 'pireSite' ne correspond

			SousGroupeSite sgs = createSousGroupe("Ss-grpe passe 2 n°" + numSousGroupe + " ("+setSiteDuSousGroupe.size()+")", setSiteDuSousGroupe);

			// On met le site de départ en 1ere position, pour qu'il soit bien vivisble lors de la sélection du SousGroupe
			//  en effet, le 1er élément est automatiquement en mode highlight et donc plus visible
			sgs.makeThisSiteFirst(pireSite);
			v_Sg_pass2.add(sgs);

			Logger.log_INFO2(" -> Site : " + pireSite + " sous-groupe compose  taille = " + setSiteDuSousGroupe.size());

			numSousGroupe++;
		}

		// dernière étape : à partir des sous-groupe, on regroupe ceux qui sont trop petits
		//  car des groupes tres petits mettent du temps à etre resolus avec l'algo :
		// il y a bcp de temps fixe (create des thread de calcul a chaque step ...) du coup
		//  les 4 CPU sont loin des 100%
		v_Sg_pass2 = regroupeSousGroupeTropPetits(v_Sg_pass2, "passe 2");
	}

	// Idem mais pour un unique site donné (pas forcément le plus mauvais, d'ailleurs)
	// => utilisé en mode interactif
	// réduit v_Sg_pass2 à 1 unique Traitement !
	public void prepareTraitement_pass2(Site site, double seuilNoteAnno, double seuilIntersectRectMax) {
		Logger.log_INFO1("Préparation du traitements de la 2ème passe pour le site : " + site);
		Logger.log_INFO1(" - seuil note = " + seuilNoteAnno);
		Logger.log_INFO1(" - seuil interaction = " + seuilIntersectRectMax);

		v_Sg_pass2 = new ArrayList<SousGroupeSite>();

		// Création d'un sous-groupe associé à ce site
		HashSet<Site> setSiteDejaExamine = new HashSet<Site>();
		HashSet<Site> setSiteDuSousGroupe = new HashSet<Site>();
		completeSousGroupeAvecSitesInteraction_rec(site, setSiteDuSousGroupe, setSiteDejaExamine, seuilIntersectRectMax);
		SousGroupeSite sgs = createSousGroupe("Ss-grpe passe 2 " + site + " ("+setSiteDuSousGroupe.size()+")", setSiteDuSousGroupe);
		sgs.makeThisSiteFirst(site);

		v_Sg_pass2.add(sgs);

		Logger.log_INFO2(" -> Site : " + site + " sous-groupe compose  taille = " + setSiteDuSousGroupe.size());
	}


	// Qques stat pour avertir l'utilisateur des traitements qui vont être induit par ses paramètres
	public int getNbSSGroupe_pass2() {
		if (v_Sg_pass2==null) return 0;
		return v_Sg_pass2.size();
	}

	public int getSizeMaxSsGroupe_pass2() {
		if (v_Sg_pass2==null) return 0;
		int sizeMax = 0;
		for (SousGroupeSite sgs : v_Sg_pass2)
			sizeMax = Math.max(sizeMax, sgs.size());
		return sizeMax;
	}



	//**************************************************************************
	// Fonctions utiles pour les regroupements en composante connexe d'interaction
	// du traitement principal ainsi que pour le post traitement

	// Des sous groupes trop petits sont penalisant a cause du temps fixe impose pour chaque iteration
	private ArrayList<SousGroupeSite> regroupeSousGroupeTropPetits(ArrayList<SousGroupeSite> v_Sg, String suffixName) {
		if (v_Sg.size()<=1) return v_Sg;

		// tri par ordre decroissant des sous-groupes selon leur taille
		Collections.sort(v_Sg);
		Collections.reverse(v_Sg);

		ArrayList<SousGroupeSite> v_Sg_new = new ArrayList<SousGroupeSite>();

		v_Sg_new.add(v_Sg.get(0).getCopy()); // on fait des copies pour que le assert de verif de checkSize AVANT/APRES fonctionne bien
		int currentIndex_new = 0;
		for (int i=1 ; i<v_Sg.size() ; i++) {
			SousGroupeSite sgs = v_Sg.get(i);

			if (v_Sg_new.get(currentIndex_new).size() >= ParamsAppli.ANNO_NB_MIN_SITE_IN_SOUSGROUPE) {
				// le dernier sous-groupe du nouveau vecteur est deja trop gros
				//  on ajoute le sous groupe en cours à la suite
				v_Sg_new.add(sgs.getCopy()); // on fait des copies pour que le assert de verif de checkSize AVANT/APRES fonctionne bien
				currentIndex_new++;
			} else {
				// le dernier sous-groupe du nouveau vecteur peut absorber le sous-groupe en cours
				v_Sg_new.get(currentIndex_new).absorbSitesFrom(sgs);
			}
		}
		assert(checkSize(v_Sg, v_Sg_new));

		// on renome les sousgroupe, dont l'ordre a changé ...
		for (int i=0; i<v_Sg_new.size() ; i++) {
			SousGroupeSite sgs = v_Sg_new.get(i);
			sgs.name = "Ss-grpe "+suffixName+" n°" + (i+1) + " ("+sgs.size()+")";
		}

		return v_Sg_new;
	}
	private boolean checkSize(ArrayList<SousGroupeSite> v_Sg1, ArrayList<SousGroupeSite> v_Sg2) {
		int size_avant=0, size_apres=0;
		for (SousGroupeSite sgs : v_Sg1) size_avant += sgs.size();
		for (SousGroupeSite sgs : v_Sg2) size_apres += sgs.size();
		assert (size_avant==size_apres) : "" + size_avant + " -> " + size_apres;
		return true;
	}


	private SousGroupeSite createSousGroupe(String name, HashSet<Site> setSiteDuSousGroupe) {
		SousGroupeSite sgs = new SousGroupeSite(name, this);
		for (Site s : setSiteDuSousGroupe)
			sgs.vIndexSite.add(s.index);
		return sgs;
	}


	// Complete un sous groupe avec les sites connexes (qui peuvent interagir avec eux)
	// si seuilInteraction <=0, les sous-groupe sont complets avec tous les sites interagissant de proche en proche  => PASSE 1
	// si seuilInteraction > 0, on arrete le sous-groupe au dela d'un certain seuil de surface d'intersection des rectMax des sites => PASSE 2
	private void completeSousGroupeAvecSitesInteraction_rec (Site site_base, HashSet<Site> setSiteDuSousGroupe, HashSet<Site> setSiteNotToTake, double seuilIntersectRectMax) {
		if (setSiteNotToTake.contains(site_base)) return; // on est deja passe par ce site

		setSiteNotToTake.add(site_base);
		if (!site_base.hasAnno()) return; // les tab d'optimisation de ce site sont null ...
		setSiteDuSousGroupe.add(site_base);
		

		// On parcourt les sites liés au site donné en entrée, rangés dans les différents tableaux issus de l'optimisation
		completeSousGroupeAvecSitesInteraction_rec_forthisTabOptimisation(site_base, site_base.tabSiteSuperposables, setSiteDuSousGroupe, setSiteNotToTake, seuilIntersectRectMax);
		completeSousGroupeAvecSitesInteraction_rec_forthisTabOptimisation(site_base, site_base.tabSiteAmbiguiteMax_Anno_Site_memeCouleur, setSiteDuSousGroupe, setSiteNotToTake, seuilIntersectRectMax);
		// ON NE PREND PAS EN COMPTE L'AMBIGUITE AUTRE COULEUR, CAR CRITERE PAS IMPORTANT -> cela permet de mieux sous-decouper
		//completeSousGroupe_rec_forthisTableOptisation(site_base, site_base.tabSiteAmbiguiteMax_Anno_Site_autreCouleur, setSiteDuSousGroupe, setSiteDejaRange);
		completeSousGroupeAvecSitesInteraction_rec_forthisTabOptimisation(site_base, site_base.tabSiteAmbiguiteMax_Site_Anno_memeCouleur, setSiteDuSousGroupe, setSiteNotToTake, seuilIntersectRectMax);
		// ON NE PREND PAS EN COMPTE L'AMBIGUITE AUTRE COULEUR, CAR CRITERE PAS IMPORTANT -> cela permet de mieux sous-decouper
		//completeSousGroupe_rec_forthisTableOptisation(site_base, site_base.tabSiteAmbiguiteMax_Site_Anno_autreCouleur, setSiteDuSousGroupe, setSiteDejaRange);
		completeSousGroupeAvecSitesInteraction_rec_forthisTabOptimisation(site_base, site_base.tab_tabSiteIntersectAnno[Site.SSRECT_FULL], setSiteDuSousGroupe, setSiteNotToTake, seuilIntersectRectMax);
	}
	private void completeSousGroupeAvecSitesInteraction_rec_forthisTabOptimisation (Site site_base, Site[] tabOptimisation, HashSet<Site> setSiteDuSousGroupe, HashSet<Site> setSiteNotToTake, double seuilIntersectRectMax) {
		for (Site site : tabOptimisation) {
			if (site==site_base) continue; // cela peut arriver si tabOptimisation est la table des sites superposables
			if (setSiteNotToTake.contains(site)) continue;

			// appel recursif vers les sites liés

			// au préalable, si seuil indiqué, alors on peut éventuellement stopper la récursion si le seuil n'est pas franchi
			if (seuilIntersectRectMax>=0) {
				double surfaceRectMax_min = Math.min(site_base.rectMax_reel_m.getSurface(), site.rectMax_reel_m.getSurface());
				double surfaceIntersect = UtilsGeom.getSurfaceRectIntersection(site_base.rectMax_reel_m, site.rectMax_reel_m);
				if (surfaceIntersect / surfaceRectMax_min < seuilIntersectRectMax)
					continue;
			}

			completeSousGroupeAvecSitesInteraction_rec (site, setSiteDuSousGroupe, setSiteNotToTake, seuilIntersectRectMax);
		}
	}


	//**************************************************************************
	//**************************************************************************

	public double getMeanPerSite_NbSegmentIntersectable(int whichSousRect) {
		double res = 0;
		for (Site s : vSite) {
			if (!s.hasAnno()) continue;
			res += ((double)s.tab_tabSegmentIntersectable[whichSousRect].length) / vSite.size();
		}
		return res;
	}

	public double getMeanPerSite_NbSiteIntersectLien(int whichSousRect) {
		double res = 0;
		for (Site s : vSite){
			if (!s.hasAnno()) continue;
			res += ((double)s.tab_tabSiteIntersectAnno[whichSousRect].length) / vSite.size();
		}
		return res;
	}

	private void computeRectEmprise() {
		if (vSite.isEmpty())
			rectEmprise = null;
		rectEmprise = new Rect_d(vSite.get(0));
		for (Site site : vSite)
			rectEmprise.extendToPoint(site);
	}
	public Rect_d getRectEmprise() {
		return rectEmprise;
	}

	public ArrayList getSitesThaMayIntersectWith(Rect_d rectVisu) {
		if (spiSites != null)
			return spiSites.getObjectsThatMayIntersectWith(rectVisu);
		else
			return new ArrayList();
	}

	private void computeLargestObject() {
		largestDimensionRectMax_reel_m = 0.0;
		largestRayon_reel_m = 0.0;

		mapLargestDimensionRectLarg = new HashMap<Zones, Double>();
		mapLargestRayon = new HashMap<Zones, Double>();

		for (Site site : vSite) {
			largestDimensionRectMax_reel_m = Math.max(largestDimensionRectMax_reel_m, site.rectMax_reel_m.getWidth());
			largestRayon_reel_m = Math.max(largestRayon_reel_m, site.rayonSite_reel_m);

			// version classée par zone
			double oldValue;
			if (mapLargestDimensionRectLarg.containsKey(site.getZones()))
				oldValue = mapLargestDimensionRectLarg.get(site.getZones());
			else
				oldValue = -1;
			double newValue = Math.max(oldValue, site.rectMax_reel_m.getWidth());
			mapLargestDimensionRectLarg.put(site.getZones(), newValue);

			if (mapLargestRayon.containsKey(site.getZones()))
				oldValue = mapLargestRayon.get(site.getZones());
			else
				oldValue = -1;
			newValue = Math.max(oldValue, site.rayonSite_reel_m);
			mapLargestRayon.put(site.getZones(), newValue);
		}
	}

	public double getLargestDimensionRectMax() {return largestDimensionRectMax_reel_m;}
	public double getLargestRayonSite() {return largestRayon_reel_m;}

	public double getLargestDimensionRectMax(Zones zones) {return mapLargestDimensionRectLarg.get(zones);}
	public double getLargestRayonSite(Zones zones) {return mapLargestRayon.get(zones);}


	// Renvoie le nombre d'anno qui ont un libelle ET dont l'échelle de généralisation > 0
	// (normalement, il ne doit pas y avoir d'echelle=0, car filtré dans les pretraitement ...)
	public int getNbAnnoToBeGeneralized() {
		int res = 0;
		
		for (Site site : vSite)
			if (site.getEchelleGeneralization().echelle_inv>0 && site.hasAnno())
				res++;
		return res;
	}
}
